package Email.auto.checker;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Scanner;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

/**
 * @author Andre Duarte - duarte.andre00@gmail.com
 * @author Mario Marques - mariomarques.01@gmail.com
 * 
 */

public class Comunication {

	/**
	 * Variable to save the IMAP Server port.
	 */
	private int serverPort = 0;
	/**
	 * SSLSocketFactory to create a SSLSocket.
	 */
	private SSLSocketFactory sslSocketFactory = null;
	/**
	 * SSLSOcket to communicate with the server.
	 */
	private SSLSocket comunicationSocket = null;
	/**
	 * Write information to the server socket.
	 */
	private PrintWriter print = null;
	/**
	 * Read information from the socket.
	 */
	private BufferedReader reader = null;

	/**
	 * Constructor.
	 */
	public Comunication() {
		sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
	}

	/**
	 * Sets the server to comunicate
	 * 
	 * @param serverHost
	 *            Name of corresponding user server account
	 * @return COMUNICATION_ERROR (-1) : error during connection to server
	 *         COMUNICATION_OK (1) : comunication with server has been
	 *         established
	 */
	public final int setServer(final String serverHost) {

		String server_answer = null;

		// Check input Parameters
		if (serverHost == null) {
			return ConstComunication.COMUNICATION_ERROR;
		}
		if (!(serverHost.equals(ConstComunication.GMAIL_SERVER))
				&& !(serverHost.equals(ConstComunication.ISEC_SERVER))) {
			return ConstComunication.COMUNICATION_ERROR;
		}

		if (serverHost.equals(ConstComunication.GMAIL_SERVER)) {
			serverPort = ConstComunication.GMAIL_PORT;

		} else {
			serverPort = ConstComunication.ISEC_PORT;
		}
		
		try {
			comunicationSocket = (SSLSocket) sslSocketFactory.createSocket(
					serverHost, serverPort);
			comunicationSocket.setSoTimeout(2000);

			print = new PrintWriter(comunicationSocket.getOutputStream());
			reader = new BufferedReader(new InputStreamReader(
					comunicationSocket.getInputStream()));

			/*
			 * Servers sends response after createSocket and we must read it
			 */
			server_answer = reader.readLine();

		} catch (UnknownHostException e) {
			return ConstComunication.COMUNICATION_ERROR;
		} catch (IOException e) {
			return ConstComunication.COMUNICATION_ERROR;
		}

		if ((server_answer != null) && server_answer.contains("OK")) {
			return ConstComunication.COMUNICATION_OK;
		} else {
			return ConstComunication.COMUNICATION_ERROR;
		}
	}

	/**
	 * Perform login on server given in setServer method
	 * 
	 * @param loginCommand1
	 *            login command to corresponding server
	 * @param loginCommand2
	 *            Additional data to login command, this parameter may be null
	 *            or contain additional data like separated password to make
	 *            login in Hotmail server
	 * 
	 * @return true: login ok false: login error
	 */
	public final boolean login(final String loginCommand1,
			final String loginCommand2) {

		String serverAnswer = null;

		// Check data before start working
		if ((loginCommand1 == null) || (print == null) || (reader == null)
				|| (comunicationSocket == null)) {
			return false;
		}

		if (!comunicationSocket.isConnected()) {
			return false;
		}

		// if there's only one command on parameters
		if (loginCommand2 == null) {
			print.println(loginCommand1);
			print.flush();

			try {
				serverAnswer = reader.readLine();
			} catch (IOException e) {
				e.printStackTrace();
				return false;
			}
		}
		// if there's two commands on parameters
		else {
			//Actually this functionality isn't implemented
		}

		if ((serverAnswer.contains(ConstComunication.IMAP_SERVER_ANSWER_ERROR))
				|| (serverAnswer
						.contains(ConstComunication.POP_SERVER_ANSWER_ERROR))) {
			return false;
		}
		return true;
	}

	/**
	 * 
	 * Gets all the unreaded messages on the account that was validated in login
	 * method.
	 * 
	 * @param domain
	 *            Domain name of the user account
	 * @return number of UnreadedMessages
	 */
	public final int getUnreadedMessages(final String domain) {
		String serverResponse = null;
		String relevantAnswer = null;

		if (domain == null || print == null) {
			return -1;
		}
		if ((domain.equals(ConstAccount.GMAIL_DOMAIN))
				|| domain.equals(ConstAccount.ISEC_DOMAIN)) {
			String commandUnseen = ConstComunication.UNSEEN_MESSAGES_IMAP;

			print.println(commandUnseen);
			print.flush();
			try {
				while ((serverResponse = reader.readLine()) != null) {
					if (containsUnseenToken(serverResponse)) {
						relevantAnswer = serverResponse;
					}
				}
			} catch (SocketTimeoutException e1) {

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (relevantAnswer != null) {
				return getIntegerFromMessage(relevantAnswer);
			}

		} else if (domain.equals(ConstAccount.HOTMAIL_DOMAIN)) {
			String commandListMessageHotmail = ConstComunication.UNSEEN_MESSAGES_POP;
			print.println(commandListMessageHotmail);
		}
		return -1;
	}

	/**
	 * Verify if a given message contains a token representant of UNSEEN
	 * message.
	 * 
	 * @param mess
	 *            Strign message to verify data
	 * @return true if message contains "UNSEEN" token in it
	 */
	private boolean containsUnseenToken(final String mess) {
		if (mess.contains("UNSEEN")) {
			return true;
		}
		return false;
	}

	/**
	 * Get a int value on the given string corresponding to number of unreaded
	 * messages.
	 * 
	 * @param mess
	 *            message to get integer value
	 * @return int value finded on string
	 */
	private int getIntegerFromMessage(final String mess) {
		int intValue;

		Scanner in = new Scanner(mess).useDelimiter("[^0-9]+");
		intValue = in.nextInt();

		return intValue;
	}

	/**
	 * Stop comunication with server.
	 */
	public final int endComunication(final String domain) {

		try {
			if ((domain != null)
					&& (print != null)
					&& ((domain.equals("gmail.com") || (domain
							.equals("isec.pt"))))) {
				print.println(ConstComunication.LOGOUT_IMAP);

				if (comunicationSocket != null) {

					try {
						comunicationSocket.close();
					} catch (IOException e) {
					}
				}
				if ((print != null) && (reader != null)) {
					print.close();
					try {
						reader.close();
					} catch (IOException e) {
					}
				}

			}
		} catch (Exception e) {
			return ConstComunication.COMUNICATION_ERROR;
		}

		return ConstComunication.COMUNICATION_OK;

	}
	
	/**
	 * Validate data in email server: this method could be called when data is readed from a file
	 * in a format: email password
	 * 
	 * @param email: String email to validate
	 * @param pass: String password to corresponding email given in first parameter
	 * @return: boolean: Success of validation on server
	 */
	public boolean validaActualUser(String email, String pass){
		String[] part= {"part1","part2"};
		String domain = null;
		String user = null;
		
		if(email == null || pass == null)
			return false;
		
		System.out.println("ValidaActualUser: " +email);
		if (email.contains("@")) {
			part = email.split("@");
			user = part[0];
			domain = part[1];
		}
		System.out.println("O domain e: "+domain);
		if(domain.equals(ConstAccount.ISEC_DOMAIN)){
			if(setServer(ConstComunication.ISEC_SERVER) == ConstComunication.COMUNICATION_OK){
				String login_command_isec = ConstComunication.LOGIN_IMAP +" "+ user + " " + pass;
				if(login(login_command_isec, null) == true){
					System.out.println("Login: Ok");
					return true;
				}
			}
		}
		else if(domain.equals(ConstAccount.GMAIL_DOMAIN)){
			if(setServer(ConstComunication.GMAIL_SERVER) == ConstComunication.COMUNICATION_OK){
				String login_command_gmail = ConstComunication.LOGIN_IMAP +" "+ user + " " + pass;
				if(login(login_command_gmail, null) == true){
					System.out.println("Login: Ok");
					return true;
				}
			}
		}

		return false;
	}
	
	
	
} // END of Comunication class
